/*
 * Copyright (C) 2008 GeoScheduler Team, as stated on <http://www.geoscheduler.org/authors>.
 * 
 * This file is part of GeoScheduler.
 * 
 * GeoScheduler is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * GeoScheduler is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License
 * along with GeoScheduler. If not, see <http://www.gnu.org/licenses/>.
 */
package org.geoscheduler.android;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import org.geoscheduler.android.exceptions.GSContextNotInitializedException;
import org.geoscheduler.android.rest.Auth;
import org.geoscheduler.android.rest.EventSynchronizer;
import org.geoscheduler.android.rest.ServerAPIVersionChecker;
import org.geoscheduler.android.rest.impl.BasicAuth;
import org.geoscheduler.android.rest.impl.ServerEventSynchronizer;
import org.geoscheduler.commons.marshaller.JSONFieldDeclFactory;
import org.geoscheduler.commons.marshaller.JSONMarshaller;
import org.geoscheduler.commons.marshaller.decls.JSONCollectionField;
import org.geoscheduler.commons.marshaller.decls.JSONDateField;
import org.geoscheduler.commons.marshaller.decls.JSONEntityField;
import org.geoscheduler.commons.marshaller.decls.JSONEnumField;
import org.geoscheduler.commons.marshaller.decls.JSONIntegerField;
import org.geoscheduler.commons.marshaller.decls.JSONLongField;
import org.geoscheduler.commons.marshaller.decls.JSONStringField;
import org.geoscheduler.commons.marshaller.impl.JSONMarshallerImpl;
import org.geoscheduler.commons.marshaller.impl.NaiveJSONMarshaller;
import org.geoscheduler.model.Account;
import org.geoscheduler.model.Token;

import android.content.Context;
import android.content.SharedPreferences;

/**
 * This is basically substitution of application context from Spring.
 * It provides initialized implementation of various service classes.
 *
 * @author srakyi
 */
public class GSContext {
	
	public final static String LOGTAG = "GeoScheduler";
	
	public final static Boolean CONF_USE_NAIVE_MARSHALLER = true;
	
	public final static String PREFS_NAME = "GeoSchedulerPrefs";
	public final static String PREFS_LOGIN = "login";
	public final static String PREFS_PASSWD = "passwd";
	public final static String PREFS_LP_REMEMBER = "lp_remember";
	public final static String PREFS_CURRENT_TOKEN = "token";
	public final static String PREFS_SERVER_URL = "server_url";
	
	public static String default_PREFS_SERVER_URL;
	
	private static Map<String, ?> prefs;
	private static Token token;
	private static Account account;
	
	private static Auth auth;
	private static JSONMarshaller marshaller;
	private static ServerAPIVersionChecker serverAPIversionChecker;
	private static EventSynchronizer eventSynchronizer;
	
// service providers
	
	/**
	 * Returns implementation of {@link Auth} interface.
	 * 
	 * @return	Ready-to use implementation.
	 */
	public static Auth getAuth() {
		if (GSContext.auth == null) {
			GSContext.auth = new BasicAuth();
		}

		return GSContext.auth;
	}
	
	/**
	 * Initializes {@link JSONMarshaller} (if not already initialized).
	 * 
	 * @return	Initialized marshaller.
	 */
	@SuppressWarnings("unchecked")
	public static JSONMarshaller getMarshaller() {
		if (GSContext.marshaller == null) {
			if (CONF_USE_NAIVE_MARSHALLER) {
				return new NaiveJSONMarshaller();
			} else {
				List<JSONFieldDeclFactory<?>> fieldDecls = new ArrayList<JSONFieldDeclFactory<?>>(8);
				fieldDecls.add(new JSONDateField.Factory());
				fieldDecls.add(new JSONEnumField.Factory());
				fieldDecls.add(new JSONStringField.Factory());
				fieldDecls.add(new JSONLongField.Factory());
				fieldDecls.add(new JSONIntegerField.Factory());
				fieldDecls.add(new JSONEntityField.Factory());
				fieldDecls.add(new JSONCollectionField.Factory());
				GSContext.marshaller = new JSONMarshallerImpl(fieldDecls);
			}
		}
		
		return GSContext.marshaller;
	}
	
	public static ServerAPIVersionChecker getServerAPIVersionProvider() {
		if (GSContext.serverAPIversionChecker == null) {
			GSContext.serverAPIversionChecker = new ServerAPIVersionChecker();
		}
		
		return GSContext.serverAPIversionChecker;
	}
	
	public static EventSynchronizer getEventSynchronizer(Context ctx) throws IOException {
		if (GSContext.eventSynchronizer == null) {
			GSContext.eventSynchronizer = new ServerEventSynchronizer(ctx);
		}
		
		return GSContext.eventSynchronizer;
	}
	
// Properties handling
	
	public static Token getToken() {
		return token;
	}
	
	public static Account getAccount() {
		return account;
	}

	public static void setTokenAccount(Token token, Account account) {
		GSContext.token = token;
		GSContext.account = account;
	}
	
// Preferences handling

	/**
	 * Loads preferences from persistent storage.
	 * 
	 * @param ctx	Current {@link Context}.
	 */
	public static void loadPrefs(Context ctx) {
		SharedPreferences shPrefs = ctx.getSharedPreferences(GSContext.PREFS_NAME, Context.MODE_PRIVATE);
		GSContext.prefs = shPrefs.getAll();

		// FIXME [srakyi] nasty hack for loading necessary default values
		GSContext.default_PREFS_SERVER_URL = ctx.getText(R.string.default_server_url).toString();
	}
	
	/**
	 * Gets value from preferences.
	 * 
	 * FIXME this is not nice at all - we'll loosing type information because of usage of Map<String, ?>!!
	 * 
	 * @param prefName	Name of preference to obtain.
	 * 
	 * @return	{@link Object} with preference value.
	 * 
	 * @throws GSContextNotInitializedException
	 */
	public static Object getPref(String prefName) throws GSContextNotInitializedException {
		if (GSContext.prefs == null) {
			throw new GSContextNotInitializedException("Preferences not loaded!");
		} else if (prefName == null) {
			return null;
		}
		
		Object ret = GSContext.prefs.get(prefName);
		
		// FIXME [srakyi] nasty way of loading defaults
		if ((GSContext.PREFS_SERVER_URL.equals(prefName)) && (ret == null)) {
			ret = GSContext.default_PREFS_SERVER_URL;
		}
		
		return ret;
	}
	
	/**
	 * Stores given preference into persistent storage.
	 * 
	 * TODO how effective is this!? (commit every time)
	 * 
	 * @param prefName		Name of preference.
	 * @param prefValue	Value to be stored.
	 * @param ctx			Current context
	 */
	public static void putPref(String prefName, String prefValue, Context ctx) {
		SharedPreferences.Editor prefsEditor = ctx.getSharedPreferences(GSContext.PREFS_NAME, Context.MODE_PRIVATE).edit();
		prefsEditor.putString(prefName, prefValue);
		prefsEditor.commit();
	}
	
	/**
	 * Same as putPref(String, String, Context)
	 */
	public static void putPref(String prefName, Long prefValue, Context ctx) {
		SharedPreferences.Editor prefsEditor = ctx.getSharedPreferences(GSContext.PREFS_NAME, Context.MODE_PRIVATE).edit();
		prefsEditor.putLong(prefName, prefValue);
		prefsEditor.commit();
	}
	
	/**
	 * Same as putPref(String, String, Context)
	 */
	public static void putPref(String prefName, Boolean prefValue, Context ctx) {
		SharedPreferences.Editor prefsEditor = ctx.getSharedPreferences(GSContext.PREFS_NAME, Context.MODE_PRIVATE).edit();
		prefsEditor.putBoolean(prefName, prefValue);
		prefsEditor.commit();
	}
	
	/**
	 * Removes given preference from persistent storage.
	 * 
	 * @param prefName	Name of preference to be removed.
	 * @param ctx		Current {@link Context}.
	 */
	public static void removePref(String prefName, Context ctx) {
		SharedPreferences.Editor prefsEditor = ctx.getSharedPreferences(GSContext.PREFS_NAME, Context.MODE_PRIVATE).edit();
		prefsEditor.remove(prefName);
		prefsEditor.commit();
	}

}
